package liquibase.datatype.core;

import java.util.Locale;
import liquibase.change.core.LoadDataChange.LOAD_DATA_TYPE;
import liquibase.database.Database;
import liquibase.database.core.*;
import liquibase.datatype.DataTypeInfo;
import liquibase.datatype.DatabaseDataType;
import liquibase.datatype.LiquibaseDataType;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.statement.DatabaseFunction;
import liquibase.util.StringUtils;

@DataTypeInfo(
        name = "boolean",
        aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"},
        minParameters = 0,
        maxParameters = 0,
        priority = 1
)
public class BooleanType extends LiquibaseDataType {
    public BooleanType() {
    }

    public DatabaseDataType toDatabaseDataType(Database database) {
        String originalDefinition = StringUtils.trimToEmpty(this.getRawDefinition());
        if (database instanceof Firebird3Database) {
            return new DatabaseDataType("BOOLEAN");
        } else if (!(database instanceof AbstractDb2Database) && !(database instanceof FirebirdDatabase)) {
            if (database instanceof MSSQLDatabase) {
                return new DatabaseDataType(database.escapeDataTypeName("bit"));
            } else if (database instanceof MySQLDatabase) {
                return originalDefinition.toLowerCase(Locale.US).startsWith("bit") ? new DatabaseDataType("BIT", this.getParameters()) : new DatabaseDataType("BIT", new Object[]{1});
            } else if (database instanceof OracleDatabase) {
                return new DatabaseDataType("NUMBER", new Object[]{1});
            } else if (database instanceof DMDatabase) {
                return new DatabaseDataType("TINYINT");
            } else if (!(database instanceof SybaseASADatabase) && !(database instanceof SybaseDatabase)) {
                if (database instanceof DerbyDatabase) {
                    return ((DerbyDatabase)database).supportsBooleanDataType() ? new DatabaseDataType("BOOLEAN") : new DatabaseDataType("SMALLINT");
                } else if (database.getClass().isAssignableFrom(DB2Database.class)) {
                    return ((DB2Database)database).supportsBooleanDataType() ? new DatabaseDataType("BOOLEAN") : new DatabaseDataType("SMALLINT");
                } else if (database instanceof HsqlDatabase) {
                    return new DatabaseDataType("BOOLEAN");
                } else {
                    return database instanceof PostgresDatabase && originalDefinition.toLowerCase(Locale.US).startsWith("bit") ? new DatabaseDataType("BIT", this.getParameters()) : super.toDatabaseDataType(database);
                }
            } else {
                return new DatabaseDataType("BIT");
            }
        } else {
            return new DatabaseDataType("SMALLINT");
        }
    }

    public String objectToSql(Object value, Database database) {
        if (value != null && !"null".equals(value.toString().toLowerCase(Locale.US))) {
            String returnValue;
            if (value instanceof String) {
                value = ((String)value).replaceAll("'", "");
                if (!"true".equals(((String)value).toLowerCase(Locale.US)) && !"1".equals(value) && !"b'1'".equals(((String)value).toLowerCase(Locale.US)) && !"t".equals(((String)value).toLowerCase(Locale.US)) && !((String)value).toLowerCase(Locale.US).equals(this.getTrueBooleanValue(database).toLowerCase(Locale.US))) {
                    if (!"false".equals(((String)value).toLowerCase(Locale.US)) && !"0".equals(value) && !"b'0'".equals(((String)value).toLowerCase(Locale.US)) && !"f".equals(((String)value).toLowerCase(Locale.US)) && !((String)value).toLowerCase(Locale.US).equals(this.getFalseBooleanValue(database).toLowerCase(Locale.US))) {
                        throw new UnexpectedLiquibaseException("Unknown boolean value: " + value);
                    }

                    returnValue = this.getFalseBooleanValue(database);
                } else {
                    returnValue = this.getTrueBooleanValue(database);
                }
            } else if (value instanceof Long) {
                if (Long.valueOf(1L).equals(value)) {
                    returnValue = this.getTrueBooleanValue(database);
                } else {
                    returnValue = this.getFalseBooleanValue(database);
                }
            } else if (value instanceof Number) {
                if (!value.equals(1) && !"1".equals(value.toString()) && !"1.0".equals(value.toString())) {
                    returnValue = this.getFalseBooleanValue(database);
                } else {
                    returnValue = this.getTrueBooleanValue(database);
                }
            } else {
                if (value instanceof DatabaseFunction) {
                    return value.toString();
                }

                if (!(value instanceof Boolean)) {
                    throw new UnexpectedLiquibaseException("Cannot convert type " + value.getClass() + " to a boolean value");
                }

                if ((Boolean)value) {
                    returnValue = this.getTrueBooleanValue(database);
                } else {
                    returnValue = this.getFalseBooleanValue(database);
                }
            }

            return returnValue;
        } else {
            return null;
        }
    }

    protected boolean isNumericBoolean(Database database) {
        if (database instanceof DerbyDatabase) {
            return !((DerbyDatabase)database).supportsBooleanDataType();
        } else if (database.getClass().isAssignableFrom(DB2Database.class)) {
            return !((DB2Database)database).supportsBooleanDataType();
        } else {
            return database instanceof Db2zDatabase || database instanceof DB2Database || database instanceof FirebirdDatabase || database instanceof MSSQLDatabase || database instanceof MySQLDatabase || database instanceof OracleDatabase || database instanceof SQLiteDatabase || database instanceof SybaseASADatabase || database instanceof SybaseDatabase;
        }
    }

    public String getFalseBooleanValue(Database database) {
        if (this.isNumericBoolean(database)) {
            return "0";
        } else {
            return database instanceof InformixDatabase ? "'f'" : "FALSE";
        }
    }

    public String getTrueBooleanValue(Database database) {
        if (this.isNumericBoolean(database)) {
            return "1";
        } else {
            return database instanceof InformixDatabase ? "'t'" : "TRUE";
        }
    }

    public LOAD_DATA_TYPE getLoadTypeName() {
        return LOAD_DATA_TYPE.BOOLEAN;
    }
}
